package com.hhf.cloud.service.impl;

import com.hhf.cloud.entities.DtxAccount;
import com.hhf.cloud.entities.DtxAccountFreeze;
import com.hhf.cloud.mapper.DtxAccountFreezeMapper;
import com.hhf.cloud.mapper.DtxAccountMapper;
import com.hhf.cloud.service.AccountTccService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

/**
 * @author Daniel-黄海峰
 * @Description
 * @create 2024/3/2 22:01
 */
@Service
@Slf4j
public class AccountTccServiceImpl implements AccountTccService {



    @Resource //账户冻结
    private DtxAccountFreezeMapper dtxAccountFreezeMapper;

    @Resource  //账户扣减
    private DtxAccountMapper dtxAccountMapper;

    /**
     * 不懂TCC的，请看详细文档：https://seata.apache.org/zh-cn/blog/seata-tcc/
     * @param userId 用户id
     * @param money 扣多少钱
     */
    @Override
    @Transactional
    public void decreaseTcc(Long userId, Long money) {
        String xid = RootContext.getXID();
        //业务悬挂判断，避免空悬挂，已经执行过回滚了就不能再预留资源
        // tcc_storage_freeze表中是否有冻结记录，等于null正确，如果有一定是CANCEL执行过，拒绝业务悬挂
        DtxAccountFreeze accountFreezeFromDB = dtxAccountFreezeMapper.selectByPrimaryKey(xid);
        if (accountFreezeFromDB != null)
        {
            //CANCEL执行过，拒绝业务往下走
            return ;
        }

        //dtx_account账户余额判断
        Example wherCondition=new Example(DtxAccount.class);
        Example.Criteria criteria=wherCondition.createCriteria();
        criteria.andEqualTo("userId",userId);
        DtxAccount account = dtxAccountMapper.selectOneByExample(wherCondition);
        if(money > account.getTotalMoney())  throw new RuntimeException("账户余额不足，预占资源失败");

        //dtx_account账户余额冻减
        dtxAccountMapper.decreaseTcc(userId,money);

        //dtx_account_freeze  tcc冻结情况
        DtxAccountFreeze accountFreeze = new DtxAccountFreeze();
        accountFreeze.setXid(RootContext.getXID());
        accountFreeze.setUserId(userId+"");
        accountFreeze.setFreezeMoney(Math.toIntExact(money));
        accountFreeze.setTccStatus(DtxAccountFreeze.TccStatusEnum.TRY);

        dtxAccountFreezeMapper.insertSelective(accountFreeze);

//        int i = 10/0;
    }

    @Override
    @Transactional
    public Boolean confirm(BusinessActionContext ctx)
    {
        System.out.println("--------come in confirm ctx.getXid(): " + ctx.getXid());
        //幂等性判断，delete操作删除1次和100次相同，天生幂等性

        //修改订单状态，从try→confirm或者你不想保留直接删除t_storage_freeze里面的TRY状态数据
        int i = dtxAccountFreezeMapper.deleteByPrimaryKey(ctx.getXid());

        return i == 1;
    }

    @Override
    @Transactional
    public Boolean cancel(BusinessActionContext ctx)
    {
        System.out.println("--------come in cancel ctx.getXid(): " + ctx.getXid());

        //dtx_account_freeze表中按照xid查冻结账户
        DtxAccountFreeze accountFreeze = dtxAccountFreezeMapper.selectByPrimaryKey(ctx.getXid());

        //空回滚判断,允许空回滚存在，冻结账户为null说明try没有执行，需要空回滚处理
        if (accountFreeze == null)
        {
            //说明try没有执行，需要空回滚处理
            DtxAccountFreeze accountFreezeNullRollBack = new DtxAccountFreeze();

            accountFreezeNullRollBack.setXid(ctx.getXid());
            accountFreezeNullRollBack.setUserId(ctx.getActionContext("userId").toString());
            accountFreezeNullRollBack.setFreezeMoney(0);
            accountFreezeNullRollBack.setTccStatus(DtxAccountFreeze.TccStatusEnum.CANCEL);

            dtxAccountFreezeMapper.insertSelective(accountFreezeNullRollBack);
            return true;
        }

        //判断幂等性
        if (accountFreeze.getTccStatus() == DtxAccountFreeze.TccStatusEnum.CANCEL)
        {
            //已经Cancel过了，不要重复处理了,保证幂等性
            return true;
        }

        //回退账户余额冻减
        Long money = Long.valueOf(accountFreeze.getFreezeMoney());
        Long userId = Long.valueOf(accountFreeze.getUserId());
        dtxAccountMapper.undecreaseTcc(userId,money);

        //冻结账户金额清零并修改状态为CANCEL
        accountFreeze.setFreezeMoney(0);
        accountFreeze.setTccStatus(DtxAccountFreeze.TccStatusEnum.CANCEL);
        int i = dtxAccountFreezeMapper.updateByPrimaryKey(accountFreeze);

        return i == 1;
    }
}



